type Resolve = (data: any) => void;
type Reject = (reason: any) => void;
type Executor = (resolve: Resolve, reject: Reject) => void;
type Status = 'pending' | 'fulfilled' | 'rejected';
type SuccessCallback = (data: any) => any;
type FailCallback = (reason: any) => any;
class MyPromise {
  private status: Status = 'pending';
  private data: any = null;
  private callbackList: [SuccessCallback, FailCallback][] = [];
  static resolve(value: any) {
    return new MyPromise((resolve, reject) => {
      if (value instanceof MyPromise) {
        value.then(resolve, reject);
      } else {
        resolve(value);
      }
    });
  }
  static reject(reason: any) {
    return new MyPromise((resolve, reject) => {
      reject(reason);
    });
  }
  static all(promiseList: (MyPromise | any)[]) {
    return new MyPromise((resolve, reject) => {
      const results = [];
      let resultCount = 0;
      for (let i = 0; i < promiseList.length; i++) {
        const item = promiseList[i];
        MyPromise.resolve(item).then((data: any) => {
          results[i] = data;
          resultCount++;
          if (resultCount === promiseList.length) {
            resolve(results);
          }
        }, reject);
      }
    });
  }
  static race(promiseList: (MyPromise | any)[]) {
    return new MyPromise((resolve, reject) => {
      for (let i = 0; i < promiseList.length; i++) {
        const item = promiseList[i];
        MyPromise.resolve(item).then(resolve, reject);
      }
    });
  }
  constructor(executor: Executor) {
    /**
     * resolve函数
     *
     * 如果status不为pending则不触发，会将status改成fulfilled
     * 循环调用successCb的时候应该放在setTimeout里
     * @param data 成功时返回的数据
     */
    const resolve = (data: any) => {
      if (this.status === 'pending') {
        this.status = 'fulfilled';
        this.data = data;
        setTimeout(() => {
          for (const [successCb, failCb] of this.callbackList) {
            successCb(this.data);
          }
        });
      }
    };
    /**
     * reject函数
     *
     * 如果status不为pending则不触发，会将status改成rejected
     * @param reason 失败时返回的数据
     */
    const reject = (reason: any) => {
      if (this.status === 'pending') {
        this.status = 'rejected';
        this.data = reason;
        setTimeout(() => {
          for (const [successCb, failCb] of this.callbackList) {
            failCb(this.data);
          }
        });
      }
    };
    try {
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }
  /**
   * then函数
   *
   * 如果已经fulfilled或rejected，则需要把task放入setTimeout
   * @param successCb 成功时回调函数，返回值为普通值或者Promise，也可以直接传递一个值，
   * @param failCb 失败回调，返回值为普通值或者Promise，也可以直接传递一个值
   */
  then(successCb: SuccessCallback | any, failCb?: FailCallback | any) {
    successCb = typeof successCb === 'function' ? successCb : (v) => v;
    failCb =
      typeof failCb === 'function'
        ? failCb
        : (v) => {
            throw v;
          };
    return new MyPromise((resolve, reject) => {
      const task = (cb: SuccessCallback | FailCallback) => {
        try {
          const newData = cb(this.data);
          if (newData instanceof MyPromise) {
            newData.then(resolve, reject);
          } else {
            resolve(newData);
          }
        } catch (e) {
          reject(e);
        }
      };
      if (this.status === 'fulfilled') {
        setTimeout(() => {
          task(successCb);
        });
      } else if (this.status === 'rejected') {
        setTimeout(() => {
          task(failCb);
        });
      } else {
        this.callbackList.push([() => task(successCb), () => task(failCb)]);
      }
    });
  }
  /**
   * catch函数
   *
   * @param failCb 失败回调，返回值为普通值或者Promise，也可以直接传递一个值
   */
  catch(failCb: FailCallback) {
    return this.then(undefined, failCb);
  }
}

console.log('start');
new MyPromise((resolve, reject) => {
  // setTimeout(() => {
  //   resolve('123');
  // }, 1000);
  resolve('123');
})
  .then((data) => {
    console.log(data);
    throw '1';
    // return new MyPromise((resolve) => {
    //   resolve('456');
    // });
  })
  .then((data) => {
    console.log(data);
  })
  .catch((e) => console.log(e));
console.log('end');
